網頁應用程式1

林嶔 (Lin, Chin)

Lesson 12

什麼是網頁應用程式(WebApp)?

– 前端:使用HTML/CSS/Java Script等技術製作網頁介面(與一般網頁無異)

– 後端:利用預先設定的模組與使用者進行互動,並回傳結果給前端

– 優勢:跨平台、更新方便

– 劣勢:速度較慢、需要穩定的連線

一個WebApp的範例

– 課程進行到這裡,你應該已經有能力寫出幾個有用的函數。如果你能學會寫WebApp,那你還能讓不會寫R語言的人也能使用你寫的這些函數。

第一節:一個簡單的WebApp(1)

– 應該還記得怎樣安裝套件吧!

F12_1

第一節:一個簡單的WebApp(2)

F12_2

第一節:一個簡單的WebApp(3)

library(shiny)

# Define UI for application that plots random distributions 
shinyUI(pageWithSidebar(

  # Application title
  headerPanel("Hello Shiny!"),

  # Sidebar with a slider input for number of observations
  sidebarPanel(
    sliderInput("obs", "Number of observations:", min = 0, max = 1000, value = 500)
  ),

  # Show a plot of the generated distribution
  mainPanel(
    plotOutput("distPlot")
  )
))

F12_3

第一節:一個簡單的WebApp(4)

library(shiny)

# Define server logic required to generate and plot a random distribution
shinyServer(function(input, output) {
  
  # Expression that generates a plot of the distribution. The expression is
  # wrapped in a call to renderPlot to indicate that:
  # 
  # 1) It is 'reactive' and therefore should be automatically re-executed
  # when inputs change 2) Its output type is a plot
  output$distPlot = renderPlot({
    
    # generate an rnorm distribution and plot it
    dist = rnorm(input$obs)
    hist(dist)
  })
  
})

F12_4

第一節:一個簡單的WebApp(5)

F12_5

F12_6

練習-1

– 如果你想複製貼上點不同的東西,可以在shiny-examples內找到更多例子。

第二節:簡單介紹shiny app架構(1)

  1. 使用者自ui.R中的給定一個參數。

  2. 這個參數傳到server.R裡面,使用反應函數進行計算。

  3. 反應完成後,再回傳至ui.R輸出反應結果。

第二節:簡單介紹shiny app架構(2)

library(shiny)

# Define UI for application that plots random distributions 
shinyUI(pageWithSidebar(

  # Application title
  headerPanel("Hello Shiny!"),

  # Sidebar with a slider input for number of observations
  sidebarPanel(
    sliderInput("obs", "Number of observations:", min = 0, max = 1000, value = 500)
  ),

  # Show a plot of the generated distribution
  mainPanel(
    plotOutput("distPlot")
  )
))
  1. headerPanel()用來定義網頁標題

  2. sidebarPanel()用來定義的控制選單內含哪些可控參數,本例中只有一個滑動輸入元件sliderInput(),元件為obs

  3. mainPanle()則是用來定義輸出區域的輸出結果,本例中只有一個圖片輸出元件plotOutput(),元件為distPlot

F12_7

第二節:簡單介紹shiny app架構(3)

library(shiny)

# Define server logic required to generate and plot a random distribution
shinyServer(function(input, output) {

    # Expression that generates a plot of the distribution. The expression is
    # wrapped in a call to renderPlot to indicate that:
    # 
    # 1) It is 'reactive' and therefore should be automatically re-executed
    # when inputs change 2) Its output type is a plot
    output$distPlot = renderPlot({

        # generate an rnorm distribution and plot it
        dist = rnorm(input$obs)
        hist(dist)
    })

})

Note:所有的輸入元件都存取在input這個List內;而所有的輸出元件都存取在output這個List內。

第二節:簡單介紹shiny app架構(4)

        obs = 500
        M = 170
        S = 10
        Coler = "blue"
        dist = rnorm(obs,mean=M,sd=S)
        hist(dist,col=Coler)

第二節:簡單介紹shiny app架構(5)

library(shiny)

# Define UI for application that plots random distributions 
shinyUI(pageWithSidebar(

  # Application title
  headerPanel("Hello Shiny!"),

  # Sidebar with inputs for number of observations, mean, SD, and coler.
  sidebarPanel(
    sliderInput("obs", "Number of observations:", min = 0, max = 1000, value = 500),
    numericInput("M", "Mean of this normal distribution:", min = -200, max = 200, value = 100),
    numericInput("S", "SD of this normal distribution:", min = 0, max = 50, value = 10),
    radioButtons("Color", "Select the color of histogram:", choices = c("Red" = "red", "Blue" = "blue", "Green" = "green"))
  ),

  # Show a plot of the generated distribution
  mainPanel(
    plotOutput("distPlot")
  )
))
library(shiny)

# Define server logic required to generate and plot a random distribution
shinyServer(function(input, output) {

    # Expression that generates a plot of the distribution. The expression is
    # wrapped in a call to renderPlot to indicate that:
    # 
    # 1) It is 'reactive' and therefore should be automatically re-executed
    # when inputs change 2) Its output type is a plot
    output$distPlot = renderPlot({

        # generate an rnorm distribution and plot it
        dist = rnorm(input$obs,mean=input$M,sd=input$S)
        hist(dist,col=input$Color)
    })

})

第三節:自訂函數與shiny app(1)

F12_8

Sig = 0.05  #two-sided significance level
p = 0.5     #The expected rate
X = 0.03    #Acceptable error range
Z = qnorm(1-Sig/2)   #The Z value based on the two-sided significance level 
n = p*(1-p)*Z^2/X^2  #Calculate the needed number of sample
n           #Print the outcome
Samplesize = function(Sig,p,X) {
  Z = qnorm(1-Sig/2)
  n = p*(1-p)*Z^2/X^2
  n
}

Samplesize(Sig = 0.05,p = 0.5, X = 0.03) #Test 1
Samplesize(Sig = 0.05,p = 0.2, X = 0.03) #Test 2
Samplesize(Sig = 0.05,p = 0.2, X = 0.05) #Test 3

第三節:自訂函數與shiny app(2)

library(shiny)

# Define UI for application that calculate the needed sample size 
shinyUI(pageWithSidebar(

  # Application title
  headerPanel("Sample size calculator"),

  # Sidebar with numeric inputs for significance level, expected rate, acceptable error range.
  sidebarPanel(
    numericInput("Sig", "Two-sided significance level:", min = 0, max = 1.000, value = 0.05),
    numericInput("p", "The expected rate:", min = 0, max = 1, value = 0.5),
    numericInput("X", "Acceptable error range:", min = 0, max = 1, value = 0.03)
  ),

  # Show the outcome
  mainPanel(
    h3(textOutput("Size"))
  )
))
library(shiny)

# Your function
Samplesize = function(Sig,p,X) {
  Z = qnorm(1-Sig/2)
  n = p*(1-p)*Z^2/X^2
  n
}

# Define server logic required to calculate the needed sample size
shinyServer(function(input, output) {

    output$Size = renderText({
      N = Samplesize(Sig = input$Sig,p = input$p, X = input$X)      
      paste("We need",round(N),"samples for this sampling.")
    })

})

練習-2

– 原始參數共有5個,分別是:

  1. alpha: 雙尾顯著水準(請在運算前把它轉換為適當的Z value)

  2. power: 單尾檢力(請在運算前把它轉換為適當的Z value)

  3. OR: 臨床上有意義之最小OR

  4. r: Case組人數是Control組人數的幾倍

  5. p0: 預期Control組之暴露率

F12_9

Samplesize_OR = function(alpha,power,OR,r,p0,corr=FALSE) {
  Za=qnorm(1-alpha/2)
  Zb=qnorm(power)
  q0=1-p0
  p1=OR*p0/(OR*p0+q0)
  q1=1-p1
  pbar=(p0+r*p1)/(r+1)
  qbar=1-pbar
  n0=(Za*sqrt((r+1)*pbar*qbar)+Zb*sqrt(r*p0*q0+p1*q1))^2/(r*(p0-p1)^2)
  n1=r*n0
  n0corr=n0/4*(1+sqrt(1+(2*(1+r)/(n1*r*sqrt((p0-p1)^2)))))^2
  n1corr=r*n0corr
  if (corr==FALSE) {return(paste("Control:",round(n0)+1,"; Case:",round(n1)+1,"; Total:",round(n0)+round(n1)+2,sep=""))}
  if (corr==TRUE) {return(paste("Control:",round(n0corr)+1,"; Case:",round(n1corr)+1,"; Total:",round(n0corr)+round(n1corr)+2,sep=""))}
}

Samplesize_OR(alpha=0.05,power=0.8,OR=1.5,r=1,p0=0.2,corr=FALSE)
Samplesize_OR(alpha=0.05,power=0.8,OR=1.5,r=1,p0=0.2,corr=TRUE)

練習-2 答案

library(shiny)

shinyUI(pageWithSidebar(
  
  headerPanel("Sample size for Case Control study"),
  
  sidebarPanel(
    numericInput("alpha", "Two-sided significance level:", min = 0.001, max = 0.999, value = 0.05),
    numericInput("power", "Power:", min = 0.001, max = 0.999, value = 0.8),
    numericInput("OR", "Smallest difference of clinical/biological importance:", min = 0.01, max = 100, value = 1.5),
    numericInput("r", "The ratio of Case/Control:", min = 0.01, max = 100, value = 1),
    numericInput("p0", "Proportion of controls with exposures:", min = 0.001, max = 0.999, value = 0.2)    
  ),
  
  mainPanel(
    h3(textOutput("text1")),
    h4(textOutput("text2")),
    h3(textOutput("text3")),
    h4(textOutput("text4"))
  )
))
library(shiny)

Samplesize_OR = function(alpha,power,OR,r,p0,corr=FALSE) {
  Za=qnorm(1-alpha/2)
  Zb=qnorm(power)
  q0=1-p0
  p1=OR*p0/(OR*p0+q0)
  q1=1-p1
  pbar=(p0+r*p1)/(r+1)
  qbar=1-pbar
  n0=(Za*sqrt((r+1)*pbar*qbar)+Zb*sqrt(r*p0*q0+p1*q1))^2/(r*(p0-p1)^2)
  n1=r*n0
  n0corr=n0/4*(1+sqrt(1+(2*(1+r)/(n1*r*sqrt((p0-p1)^2)))))^2
  n1corr=r*n0corr
  if (corr==FALSE) {return(paste("Control:",round(n0)+1,"; Case:",round(n1)+1,"; Total:",round(n0)+round(n1)+2,sep=""))}
  if (corr==TRUE) {return(paste("Control:",round(n0corr)+1,"; Case:",round(n1corr)+1,"; Total:",round(n0corr)+round(n1corr)+2,sep=""))}
}

shinyServer(function(input, output) {
  
  output$text1 = renderText({
    "Sample size without continuity correction:"
  })
  
  output$text2 = renderText({
    Samplesize_OR(alpha=input$alpha,power=input$power,OR=input$OR,r=input$r,p0=input$p0)
  })
  
  output$text3 = renderText({
    "Sample size with continuity correction:"
  })
  
  output$text4 = renderText({
    Samplesize_OR(alpha=input$alpha,power=input$power,OR=input$OR,r=input$r,p0=input$p0,corr=TRUE)
  })
  
})

第四節:conditionalPanel的應用(1)

– conditionalPanel()可以讓我們的控制bar僅在特定條件下出現

– 常態分布必須要有兩個參數(平均數&標準差)決定,而t分布則必須要由一個參數決定(自由度),因此我們就必須設計當選擇常態分不時,控制區要出現的是平均數&標準差;若選擇了t分布,控制區要出現的則是自由度

library(shiny)
shinyUI(pageWithSidebar(
  
  headerPanel("Generating distribution"),
  
  sidebarPanel(
    selectInput("method", "Choose distribution:", choices=c("Normal"="norm", "Student t"="st")),
    helpText("Setting parameter(s) for distribution model"),
    conditionalPanel(condition="input.method=='norm'",
                     numericInput(inputId="mu", label="mean", value=0),
                     numericInput(inputId="sd", label="standard deviation", value=1, min=0)
    ),
    conditionalPanel(condition="input.method=='st'",
                     numericInput(inputId="df", label="Df", value=10, min=1)
    ),
    sliderInput(inputId="obs", 
                label="Number of observations:", 
                min = 1, max = 1000, value = 500)
  ),
  
  mainPanel(
    plotOutput("distPlot")
  )

))
library(shiny)

shinyServer(function(input, output) {
    output$distPlot <- renderPlot({
        if (input$method == "norm") 
            {dist <- rnorm(input$obs, mean = input$mu, sd = input$sd)}
        if (input$method == "st") 
            {dist <- rt(input$obs, df = input$df)}
        hist(dist)
    })
})

練習-3

練習-3 答案

library(shiny)

shinyUI(pageWithSidebar(
  
  headerPanel("Sample size calculator"),
  
  sidebarPanel(
    selectInput("method", "Choose a method:", choices=c("Rate"="rate", "Case Control Study"="cc")),
    conditionalPanel(condition="input.method=='rate'",
    numericInput("Sig", "Two-sided significance level:", min = 0, max = 1.000, value = 0.05),
    numericInput("p", "The expected rate:", min = 0, max = 1, value = 0.5),
    numericInput("X", "Acceptable error range:", min = 0, max = 1, value = 0.03)
    ),
    conditionalPanel(condition="input.method=='cc'",
    numericInput("alpha", "Two-sided significance level:", min = 0.001, max = 0.999, value = 0.05),
    numericInput("power", "Power:", min = 0.001, max = 0.999, value = 0.8),
    numericInput("OR", "Smallest difference of clinical/biological importance:", min = 0.01, max = 100, value = 1.5),
    numericInput("r", "The ratio of Case/Control:", min = 0.01, max = 100, value = 1),
    numericInput("p0", "Proportion of controls with exposures:", min = 0.001, max = 0.999, value = 0.2) 
    )  
  ),
  
  mainPanel(
    h3(textOutput("text1")),
    h4(textOutput("text2")),
    h3(textOutput("text3")),
    h4(textOutput("text4"))
  )
))
library(shiny)

Samplesize = function(Sig,p,X) {
  Z = qnorm(1-Sig/2)
  n = p*(1-p)*Z^2/X^2
  n
}

Samplesize_OR = function(alpha,power,OR,r,p0,corr=FALSE) {
  Za=qnorm(1-alpha/2)
  Zb=qnorm(power)
  q0=1-p0
  p1=OR*p0/(OR*p0+q0)
  q1=1-p1
  pbar=(p0+r*p1)/(r+1)
  qbar=1-pbar
  n0=(Za*sqrt((r+1)*pbar*qbar)+Zb*sqrt(r*p0*q0+p1*q1))^2/(r*(p0-p1)^2)
  n1=r*n0
  n0corr=n0/4*(1+sqrt(1+(2*(1+r)/(n1*r*sqrt((p0-p1)^2)))))^2
  n1corr=r*n0corr
  if (corr==FALSE) {return(paste("Control:",round(n0)+1,"; Case:",round(n1)+1,"; Total:",round(n0)+round(n1)+2,sep=""))}
  if (corr==TRUE) {return(paste("Control:",round(n0corr)+1,"; Case:",round(n1corr)+1,"; Total:",round(n0corr)+round(n1corr)+2,sep=""))}
}

shinyServer(function(input, output) {
  
  output$text1 = renderText({
    if (input$method=="rate") {
      N = Samplesize(Sig = input$Sig,p = input$p, X = input$X)      
      return(paste("We need",round(N),"samples for this sampling."))
     }
    if (input$method=="cc") {"Sample size without continuity correction:"}
  })
  
  output$text2 = renderText({
    if (input$method=="rate") {return(NULL)}
    if (input$method=="cc") {Samplesize_OR(alpha=input$alpha,power=input$power,OR=input$OR,r=input$r,p0=input$p0)}
  })
  
  output$text3 = renderText({
    if (input$method=="rate") {return(NULL)}
    if (input$method=="cc") {"Sample size with continuity correction:"}
  })
  
  output$text4 = renderText({
    if (input$method=="rate") {return(NULL)}
    if (input$method=="cc") {Samplesize_OR(alpha=input$alpha,power=input$power,OR=input$OR,r=input$r,p0=input$p0,corr=TRUE)}
  })
  
})

小結

  1. 撰寫App(ui.R以及server.R)
  2. 在R內自創函數
  3. 利用conditionalPanel()增加使用者介面的自由度